# -*- coding: utf-8 -*-
import gevent
import network
import server
import pogre
import pyrec
import gui
import math
from common import Vector3f


class Camera:

  def __init__ (self):
    self.eye = Vector3f ()
    self.center = Vector3f ()
    self.angle = 0.0
    self.speed = 0.4
    self.zoom = 8.0
    self.char = None
    self.keys ={pogre.KC_W : False,
                pogre.KC_S : False,
                pogre.KC_A : False,
                pogre.KC_D : False}
    self.rotation = False

  def update (self):
    if self.char:
      x, y, z = self.char.position ()
      self.center.x = x
      self.center.y = y
      self.center.z = z
    ds = math.sin (self.angle / (180.0 / math.pi))
    dc = math.cos (self.angle / (180.0 / math.pi))
    self.eye.x = self.center.x + self.zoom * ds
    self.eye.y = self.zoom
    self.eye.z = self.center.z + self.zoom * dc
    cx, cy, cz = self.center.values ()
    ex, ey, ez = self.eye.values ()
    pogre.camera_set_position (ex, ey*1.5, ez)
    pogre.camera_look_at (cx, cy, cz)

  def handle (self, event):
    if event == pogre.MOUSE_MOTION:
      self.mouse_pos = pogre.mouse_position ()
      if self.rotation:
        x, y = pogre.event_motion ()
        self.rotate (y)
    elif event == pogre.MOUSE_BUTTON_DOWN:
      button = pogre.event_button ()
      if button == pogre.MOUSE_WHEEL_UP:
        self.zoom_in (1)
      elif button == pogre.MOUSE_WHEEL_DOWN:
        self.zoom_out (1)
      elif button == pogre.MOUSE_WHEEL_BUTTON:
        self.rotation = True
    elif event == pogre.MOUSE_BUTTON_UP:
      button = pogre.event_button()
      if button == pogre.MOUSE_WHEEL_BUTTON:
        self.rotation = False
    elif event == pogre.KEY_DOWN:
      key = pogre.event_keycode()
      if key in self.keys:
        self.keys[key] = True
    elif event == pogre.KEY_UP:
      key = pogre.event_keycode()
      if key in self.keys:
        self.keys[key] = False

  def zoom_in (self, value):
    self.zoom -= value
    if self.zoom < 1: self.zoom = 1

  def zoom_out (self, value):
    self.zoom += value
    if self.zoom > 199: self.zoom = 199

  def rotate (self, angle):
    self.angle += angle

class Character:

    def __init__ (self):
        self.id = None
        self.vel = Vector3f ()
        self.dvel = Vector3f ()
        self.pos = Vector3f ()
        # gfx
        self.body = None

        self.hp_cur = 0
        self.hp_max = 0
        self.action = None

        # GUI
        self.label = None
        self.lb_cast = None
        self.lb_cooldown = None

    def position (self):
        return self.pos.values ()

    def __str__ (self):
        return "{Character id:%s}" % self.id

class Application (object):

    def __init__ (self):
        self.connection = None
        self.connector = network.Connector ()
        self.server = server.GameServer ()
        self.game_port = 7056
        address = "0.0.0.0", self.game_port
        self.acceptor = network.Acceptor (address)
        self.state = "main_menu"
        
        self.scene_config = {}
        self.geomesh = pyrec.Geomesh ()
        self.navmesh = pyrec.Navmesh ()

    def log (self, text):
        print (text)

    def init_gui (self):
        style = gui.Style ()
        style.font = pogre.load_font ("./data/fonts/ter12.pcf.gz", 12)

        log_style = gui.Style ()
        log_style.font = style.font
        log_style.bgcolor = 0.0, 0.0, 0.0, 0.4

        self.ui = gui.Widget (style)

        # common
        ui = gui.Widget (style)
        
        log_label = gui.LogLabel (log_style)
        log_label.max_lines = 10
        log_label.position = 18, self.sheight - 124 - 48
        log_label.attach_to (ui)
        self.client_log_label = log_label
        button = gui.Button (style)
        button.position = 0, self.sheight - 128 - 48
        button.size = 16, 120 + 8
        button.set_text ( "\n".join ("client log"))
        button.attach_to (ui)
        def callback (btn):
            self.client_log_label.set_visible (not self.client_log_label.visible)
        button.callback = callback

        log_label = gui.LogLabel (log_style)
        log_label.max_lines = 10
        log_label.position = 18, self.sheight - 254 - 48
        log_label.attach_to (ui)
        self.server_log_label = log_label
        button = gui.Button (style)
        button.position = 0, self.sheight - 258 - 48
        button.size = 16, 120 + 8
        button.set_text ( "\n".join ("server log"))
        button.attach_to (ui)
        def callback (btn):
            self.server_log_label.set_visible (not self.server_log_label.visible)
        button.callback = callback

        label = gui.Label (log_style)
        label.position = 0, self.sheight - 48
        label.set_text ("")
        label.attach_to (ui)
        self.status_label = label

        self.log = self.client_log_label.append
        self.server.log = self.server_log_label.append

        self.common_ui = ui
        self.common_ui.attach_to (self.ui)

        # main_menu
        style = gui.Style ()
        style.font = pogre.load_font ("./data/fonts/DejaVuSans.ttf", 11)

        ui = gui.Widget (style)

        self.enter_game_btn = None
        self.cancel_game_btn = None

        button = gui.Button (style)
        button.size = 128, 20
        button.position = self.swidth / 2 - 64, self.sheight / 2 + 100
        button.set_text ("Обождите...")
        button.set_visible (False)
        button.attach_to (ui)
        def callback (btn):
            pass
            #self.enter_game_btn.visible = False
            # self.cancel_game_btn.visible = True
        button.callback = callback
        self.cancel_game_btn = button

        button = gui.Button (style)
        button.size = 128, 20
        button.position = 0, -24
        button.set_text ("Отставить!")
        button.attach_to (self.cancel_game_btn)
        def callback (btn):
            self.leave_game ()
        button.callback = callback

        button = gui.Button (style)
        button.size = 128, 20
        button.position = self.swidth / 2 - 64, self.sheight / 2 + 100
        button.set_text ("Войти в игру")
        button.attach_to (ui)
        def callback (btn):
            host = self.edit_address.label.text
            address = host, self.game_port
            self.enter_game (address)
            self.enter_game_btn.set_visible (False)
            self.cancel_game_btn.set_visible (True)
        self.enter_game_btn = button
        button.callback = callback

        edit = gui.EditLine (style)
        edit.size = 128, 20
        edit.position = 0, -24
        edit.set_text ("127.0.0.1")
        self.edit_address = edit
        edit.attach_to (button)

        edit = gui.EditLine (style)
        edit.size = 128, 20
        edit.position = 0, -48
        edit.set_text ("Player_n%.3f" % pogre.time ())
        edit.attach_to (button)
        self.edit_name = edit


        self.stop_gameserver = None
        self.start_gameserver = None
        self.scene_name = None

        button = gui.Button (style)
        button.size = 128, 20
        button.position = self.swidth / 2 - 64, self.sheight / 2
        button.set_text ("Остановить игру")
        button.attach_to (ui)
        button.set_visible (False)
        def callback (btn):
            self.start_gameserver.set_visible (True)
            self.stop_gameserver.set_visible (False)
            self.stop_game ()
        button.callback = callback
        self.stop_gameserver = button

        button = gui.Button (style)
        button.size = 128, 20
        button.position = self.swidth / 2 - 64, self.sheight / 2 
        button.set_text ("Создать игру")
        button.attach_to (ui)
        def callback (btn):
            self.start_gameserver.set_visible (False)
            self.stop_gameserver.set_visible (True)
            scene_name = self.scene_name.head.text
            self.start_game (scene_name)
        button.callback = callback
        self.start_gameserver = button

        combo = gui.ComboBox (style)
        combo.size = 128, 20
        combo.position = 0, -24
        import os
        for filename in os.listdir ("./data/scenes/"):
            name, ext = filename.split (".")
            if ext == "json":
                combo.append_item (name)
        combo.set_item (0)
        combo.attach_to (button)
        self.scene_name = combo
        
        self.main_menu_ui = ui
        self.main_menu_ui.set_visible (False)
        self.main_menu_ui.attach_to (self.ui)


    def run (self):
        self.swidth = 1280
        self.sheight = 768
        pogre.init_video (self.swidth, self.sheight, pogre.WINDOWED)
        
        done = False

        self.init_gui ()

        pogre.add_resource_location ("./data/meshes/")
        pogre.initialise_resources ()

        camera = Camera ()
        self.init_main_menu ()

        pogre.light_set_ambient (0.5, 0.5, 0.5, 1.0)
        pogre.light_create ("light1", 1.0, 1.0, 1.0)
        pogre.light_set_position ("light1", 10, 10, 10)

        while not done:
            self.check_status ()
            event = pogre.poll_event ()
            while event:
                done = event is pogre.QUIT
                if self.ui.handle (event): continue
                camera.handle (event)
                event = pogre.poll_event ()
            camera.update ()
            pogre.render_frame ()
            self.main_menu ()
            pogre.begin_ortho ()
            self.ui.draw ()
            pogre.end_ortho ()
            pogre.display ()
            gevent.sleep (0.01)

    def check_status (self):
        text = ""
        if self.server.running:
            text += "[+] server\n"
        else:
            text += "[-] server\n"
        if self.acceptor.started:
            text += "[+] acceptor (port:%i)\n" % self.game_port
        else:
            text += "[-] acceptor\n"
        if self.connection:
            text += "[+] connection to %s\n" % str(self.connection.id)
        else:
            text += "[-] connection\n"
        if self.connector.started:
            text += "[+] connector\n" # (status:%s)\n" % self.connector.status
        else:
            text += "[-] connector\n" #" (status:%s)\n" % self.connector.status
        self.status_label.set_text (text)

    def init_main_menu (self):
        self.main_menu_ui.set_visible (True)

    def start_game (self, scene_name):
        self.server.load_scene ("./data/scenes/%s.json" % scene_name)
        self.server.start ()
        self.acceptor.set_handle (self.server.new_connection)
        self.acceptor.start ()

    def stop_game (self):
        self.server.stop ()
        self.acceptor.stop ()

    def enter_game (self, address):
        self.connector.start (address)
        self.log ("--- establish connection to %s:%i" % address)

    def leave_game (self):
        self.log ("--- disconnect")
        self.connector.stop ()
        if self.connection:
            self.connection.close ()

    def main_menu (self):
        if self.connection and self.connection.lost:
            self.log ("--- lost %s" % self.connection)
            self.connection = None
            self.enter_game_btn.set_visible (True)
            self.cancel_game_btn.set_visible (False)

        connection = self.connector.get_connection ()
        if connection:
            self.connection = connection
            connection.start ()
            self.log ("--- new %s" % connection)
            self.log ("--- login")
            name = self.edit_name.label.text
            message = "login", name
            self.connection.send (message)

        if self.connector.status.startswith ("EEE"):
            self.log (self.connector.status)
            self.connector.status = "idle"
            self.enter_game_btn.set_visible (True)
            self.cancel_game_btn.set_visible (False)

        if self.connection:
            self.handle_messages ()

    def handle_messages (self):
        messages = self.connection.messages
        while messages:
            message = messages.pop (0)
            try:
                name = message [0]
                args = message [1:]
                method = self.__getattribute__(name)
                method (*args)
            except Exception as e:
                self.log ("EEE message:%s %s" % (str(message), e))

    def identify (self, name):
        self.log ("identify: %s" % name)

    def login (self, result):
        self.log ("login: %s" % result)

    def load_scene (self, config, characters):
        self.scene_config = config
        self.log ("--> load scene")
        self.log ("--- init geom: %s" % config["geom"])
        self.geomesh.load (config ["geom"])
        self.log ("--- init navmesh")
        for key in config ["navmesh"]:
            setattr (self.navmesh, key, config["navmesh"][key])
        self.navmesh.build_from (self.geomesh)
        self.scene = "scene"
        pogre.mesh_create (self.scene, "scene.mesh")
        self.log ("--- characters in scene")
        for char in characters:
            self.char_create (*char)
        self.log ("<-- load scene done.")

    def char_motion (self, *args):
        pass

    def char_create (self, id, position, attrs):
        char = Character ()
        #char.__dict__.update (attrs)
        char.id = id
        char.body = str(id)
        pogre.mesh_create (char.body, "body.mesh")
        pogre.mesh_set_position (char.body, *position)
        # GUI
        #self._characters [id] = char
        #if id == self.pl_char_id:
        #    self.camera.char = char

if __name__ == "__main__":
   Application ().run ()